/*
* @features: 功能
* @description: 说明
* @Date: 2021-09-29 00:01:14
* @Author: judu233(769471424@qq.com)
 * @LastEditTime: 2021-10-10 15:32:13
 * @LastEditors: judu233
*/

import c3d from "../../cc";

declare module "cc" {
    export namespace Out {
        type Key = OutExtends.Key;

        /** 开始计时 */
        export function start(): void;

        /** 打印范围内时间消耗 */
        export function end(): void;

        /** 打印表格 */
        export function table(msg: any, describe?: string): void;

        /** 无颜色日志 */
        export function trace(msg: any, describe?: string): void;

        /** 网络层数据日志 */
        export function logNet(msg: any, describe?: string): void;

        /** 客户端数据结构层日志 */
        export function logModel(msg: any, describe?: string): void;

        /** 客户端数据结构层日志 */
        export function logBusiness(msg: any, describe?: string): void;

        /** 客户端数据结构层日志 */
        export function logView(msg: any, describe?: string): void;

        /** 客户端配置数据 */
        export function logConfig(msg: any, describe?: string): void;

        export function log(msg: string): void;
        export function erroring(msg: string): void;
        export function warning(msg: string): void;
    }
}

namespace OutExtends {
    var tags: Map<string, boolean> = new Map<string, boolean>();

    export enum Key {
        /** 网络层日志 */
        Net = 'Net',
        /** 数据结构层日志 */
        Model = "Model",
        /** 业务逻辑层日志 */
        Business = "Business",
        /** 视图层日志 */
        View = "View",
        /** 配置数据 */
        Config = "Config",
        /** 登录日志 */
        Trace = 'Trace'
    }

    export function init() {
        register(Key.Net);
        register(Key.Model);
        register(Key.Business);
        register(Key.View);
        register(Key.Config);
        register(Key.Trace);
    }

    /** 开始计时 */
    export function start() {
        console.time("Time");
    }

    /** 打印范围内时间消耗 */
    export function end() {
        console.timeEnd("Time");
    }

    /** 打印表格 */
    export function table(msg: any, describe?: string) {
        if (!tags.has(Key.Trace)) {
            return;
        }
        console.table(msg);
    }

    /** 无颜色日志 */
    export function trace(msg: any, describe?: string) {
        print(Key.Trace, msg, "", describe)
    }

    /** 网络层数据日志 */
    export function logNet(msg: any, describe?: string) {
        orange(Key.Net, msg, describe);
    }

    /** 客户端数据结构层日志 */
    export function logModel(msg: any, describe?: string) {
        violet(Key.Model, msg, describe);
    }

    /** 客户端数据结构层日志 */
    export function logBusiness(msg: any, describe?: string) {
        blue(Key.Business, msg, describe);
    }

    /** 客户端数据结构层日志 */
    export function logView(msg: any, describe?: string) {
        green(Key.View, msg, describe);
    }

    /** 客户端配置数据 */
    export function logConfig(msg: any, describe?: string) {
        gray(Key.Config, msg, describe);
    }
    export function log(msg: string) {
        log(`[log]${msg}`);
    }

    export function erroring(msg: string) {
        c3d.error('[ERROR]' + msg);
    }

    export function warning(msg: string) {
        c3d.warn('[WARN]:' + msg);
    }

    // 橙色
    function orange(tag: string, msg: any, describe?: string) {
        print(tag, msg, "color:#ee7700;", describe)
    }

    // 紫色
    function violet(tag: string, msg: any, describe?: string) {
        print(tag, msg, "color:Violet;", describe)
    }

    // 蓝色
    function blue(tag: string, msg: any, describe?: string) {
        print(tag, msg, "color:#3a5fcd;", describe)
    }

    // 绿色
    function green(tag: string, msg: any, describe?: string) {
        print(tag, msg, "color:green;", describe)
    }

    // 灰色
    function gray(tag: string, msg: any, describe?: string) {
        print(tag, msg, "color:gray;", describe)
    }

    function register(tag: Key) {
        if (!tags.has(tag)) {
            tags.set(tag, true);
        }
    }

    function print(tag: string, msg: any, color: string, describe?: string) {
        if (!tags.has(tag)) {
            // 标记没有打开，不打印该日志
            return;
        }
        var backLog = console.log || log;
        if (describe) {
            backLog.call(null, "%c%s%s%s:%s%o", color, getDateString(), '[' + tag + ']', stack(5), describe, msg);
        }
        else {
            backLog.call(null, "%c%s%s%s:%o", color, getDateString(), '[' + tag + ']', stack(5), msg);
        }
    }

    function stack(index: number): string {
        var e = new Error();
        var lines = e.stack!.split("\n");
        var result: Array<any> = [];
        lines.forEach((line) => {
            line = line.substring(7);
            var lineBreak = line.split(" ");
            if (lineBreak.length < 2) {
                result.push(lineBreak[0]);
            }
            else {
                result.push({ [lineBreak[0]]: lineBreak[1] });
            }
        });

        var list: string[] = [];
        var splitList: Array<string> = [];
        if (index < result.length - 1) {
            var value: string;
            for (var a in result[index]) {
                var splitList = a.split(".");

                if (splitList.length == 2) {
                    list = splitList.concat();
                }
                else {
                    value = result[index][a];
                    var start = value!.lastIndexOf("/");
                    var end = value!.lastIndexOf(".");
                    if (start > -1 && end > -1) {
                        var r = value!.substring(start + 1, end);
                        list.push(r);
                    }
                    else {
                        list.push(value);
                    }
                }
            }
        }

        if (list.length == 1) {
            return "[" + list[0] + ".ts]";
        }
        else if (list.length == 2) {
            return "[" + list[0] + ".ts->" + list[1] + "]";
        }
        return "";
    }

    function getDateString(): string {
        let d = new Date();
        let str = d.getHours().toString();
        let timeStr = "";
        timeStr += (str.length == 1 ? "0" + str : str) + ":";
        str = d.getMinutes().toString();
        timeStr += (str.length == 1 ? "0" + str : str) + ":";
        str = d.getSeconds().toString();
        timeStr += (str.length == 1 ? "0" + str : str) + ":";
        str = d.getMilliseconds().toString();
        if (str.length == 1) str = "00" + str;
        if (str.length == 2) str = "0" + str;
        timeStr += str;

        timeStr = "[" + timeStr + "]";
        return timeStr;
    }
}

/**日志等级 */
export enum LogLevel {
    DEBUG = 0X00000001,
    DUMP = 0X00000010,
    WARN = 0X00000100,
    ERROR = 0X00001000,
    ALL = DEBUG | DUMP | WARN | ERROR,
}

/**
 * 界面视图状态
 */
export enum ViewStatus {
    /**等待关闭 */
    WAITTING_CLOSE,
    /**等待隐藏 */
    WATITING_HIDE,
    /**无状态 */
    WAITTING_NONE,
}

export enum ButtonSpriteType {
    Norml = "normalSprite",
    Pressed = "pressedSprite",
    Hover = "hoverSprite",
    Disable = "disabledSprite",
}


export class LoggerImpl {
    private _level: number = LogLevel.ALL;
    constructor() { this.update(); }

    /**当前日志等级 */
    public get level() { return this._level; }
    public set level(level) {
        this._level = level;
        this.update();
    }

    e(...data: any[]) { }
    d(...data: any[]) { }
    w(...data: any[]) { }
    dump(...data: any[]) { }

    /**
     * 附加日志输出类型
     * @param level 
     */
    public attach(level: LogLevel) {
        if (this.isValid(level)) {
            return;
        }
        this.level = this.level | level;
        this.update();
    }

    /**
     * 分离日志输出类型
     **/
    public detach(level: LogLevel) {
        if (this.isValid(level)) {
            this.level = this.level ^ level;
            this.update();
        }
    }

    /**当前日志等级是否生效 */
    public isValid(level: LogLevel) {
        if (this.level & level) {
            return true;
        }
        return false;
    }

    /**更新日志 */
    private update() {
        if (this.isValid(LogLevel.DUMP)) {
            if (c3d.sys.isBrowser) {
                this.dump = console.log;
            } else {
                this.dump = this.dumpS.bind(this);
            }
        } else {
            this.dump = () => { }
        }
        if (this.isValid(LogLevel.ERROR)) {
            this.e = console.error;
        } else {
            this.e = () => { };
        }
        if (this.isValid(LogLevel.DEBUG)) {
            this.d = console.log;
        } else {
            this.d = () => { };
        }
        if (this.isValid(LogLevel.WARN)) {
            this.w = console.warn;
        } else {
            this.w = () => { };
        }
    }

    private dumpS() {
        if (this.isValid(LogLevel.DUMP)) {
            let ret = this._dump(arguments[0], arguments[1], arguments[2], arguments[4]);
            this.d(ret);
        }
    }

    private _dump(data: any, name: string = "unkown", level: number = 10, deep: number = 0): string {
        if (level < 0) {
            return "..."
        }
        deep = deep + 3;
        let self = this;
        let do_boolean = function (v: boolean) {
            return 'Boolean(1) ' + (v ? 'TRUE' : 'FALSE');
        };
        let do_number = function (v: number) {
            return v;
        };
        let do_string = function (v: string) {
            return '"' + v + '"';
        };
        let do_object = function (v: any) {
            if (v === null) {
                return "NULL(0)";
            }
            let out = '';
            let num_elem = 0;
            let indent = '';

            if (v instanceof Array) {
                num_elem = v.length;
                for (let d = 0; d < deep; ++d) {
                    indent += ' ';
                }
                out = "Array(" + num_elem + ") " + (indent.length === 0 ? '' : '') + "[";
                for (let i = 0; i < num_elem; ++i) {
                    out += "\n" + (indent.length === 0 ? '' : '' + indent) + "   [" + i + "] = " + self._dump(v[i], '', level - 1, deep);
                }
                out += "\n" + (indent.length === 0 ? '' : '' + indent + '') + "]";
                return out;
            } else if (v instanceof Object) {
                for (let d = 0; d < deep; ++d) {
                    indent += ' ';
                }
                out = "{";
                for (let p in v) {
                    out += "\n" + (indent.length === 0 ? '' : '' + indent) + "   [" + p + "] = " + self._dump(v[p], '', level - 1, deep);
                }
                out += "\n" + (indent.length === 0 ? '' : '' + indent + '') + "}";
                return out;
            } else {
                return 'Unknown Object Type!';
            }
        };
        name = typeof name === 'undefined' ? '' : name;
        let out = '';
        let v_name = '';
        switch (typeof data) {
            case "boolean":
                v_name = name.length > 0 ? name + ' = ' : '';
                out += v_name + do_boolean(data);
                break;
            case "number":
                v_name = name.length > 0 ? name + ' = ' : '';
                out += v_name + do_number(data);
                break;
            case "string":
                v_name = name.length > 0 ? name + ' = ' : '';
                out += v_name + do_string(data);
                break;
            case "object":
                v_name = name.length > 0 ? name + ' => ' : '';
                out += v_name + do_object(data);
                break;
            case "function":
                v_name = name.length > 0 ? name + ' = ' : '';
                out += v_name + "Function";
                break;
            case "undefined":
                v_name = name.length > 0 ? name + ' = ' : '';
                out += v_name + "Undefined";
                break;
            default:
                out += v_name + ' is unknown type!';
        }
        return out;
    }
}


window[`cc`][`Out`] = OutExtends;
OutExtends.init();

